﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.ServiceModel;
using System.Web;
using Newtonsoft.Json;
using SecurityClient;
using IParser = SecurityModule.Parsers.IParser;
using ParseResultItem = SecurityModule.Parsers.ParseResultItem;

namespace SecurityModule.Workflows
{
    class WFPostAppDataUploading : SimpleWorkflow
    {
        public WFPostAppDataUploading()
        {
            Verb = "POST";
            Path = "/App/DataUploading";
        }

        public override void PreProcessImplementation(HttpContext context)
        {
            var callback = new Func<Stream, Stream, bool>((inputStream, outputStream) =>
            {

                //#if DEBUG
                //                //Write request to a file
                //                var filename = string.Format("post_{0:N}.txt", Guid.NewGuid());
                //                var fs = new FileStream("c:\\" + filename, FileMode.CreateNew);
                //                inputStream.CopyTo(fs);
                //                fs.Close();
                //                inputStream.Seek(0, SeekOrigin.Begin);
                //#endif

                //Parse multi-part request stream, and then rewrite it
                var multipart = new MultipartFormParser.MultipartFormParser();
                var fields = multipart.Parse(inputStream, context.Request.ContentEncoding);
                try
                {
                    //If there is a file upload then lets process it
                    if (fields.ContainsKey("File") && fields["File"].Bytes != null)
                    {
                        //Parse the file upload
                        var csv = context.Request.ContentEncoding.GetString(fields["File"].Bytes);

                        // ReSharper disable PossibleNullReferenceException
                        var allParsers = GetType()
                            .Assembly
                            .GetTypes()
                            .Where(t => t.IsClass &&
                                        !t.IsAbstract &&
                                        typeof(IParser).IsAssignableFrom(t) &&
                                        t.GetConstructor(Type.EmptyTypes) != null)
                            .Select(t => (IParser)(t.GetConstructor(Type.EmptyTypes).Invoke(new object[] { })));
                        // ReSharper restore PossibleNullReferenceException
                        var parser = allParsers.FirstOrDefault(p => p.CanParse(csv));

                        try
                        {
                            //Read CSV file, parse, encrypt, and return as JSON
                            if (parser != null)
                            {
                                var acceptedResult = parser.Parse(csv);
                                if (acceptedResult != null)
                                {
                                    //Encrypt the tax dollar figures
                                    var security = new SecurityServiceProxy();
                                    var taxItems = new List<decimal>();
                                    foreach (var r in acceptedResult)
                                    {
                                        if (r.TaxItems != null && r.TaxItems.Any())
                                        {
                                            foreach (var taxitem in r.TaxItems)
                                            {
                                                taxItems.Add(decimal.Parse(taxitem.Value));
                                            }
                                        }
                                    }

                                    var taxItemsArray = taxItems.ToArray();
                                    var encryptionResult = ChunkArrayOperation.BulkEncryptInChuncks(security,
                                                                                                    taxItemsArray, 500);
                                    if (encryptionResult.Success)
                                    {
                                        var i = 0;
                                        foreach (var r in acceptedResult)
                                        {
                                            if (r.TaxItems != null && r.TaxItems.Any())
                                            {
                                                foreach (var taxitem in r.TaxItems)
                                                {
                                                    taxitem.Value = encryptionResult.EncryptedValues[i];
                                                    i++;
                                                }
                                            }
                                        }
                                    }
                                    else
                                        throw new Exception(encryptionResult.ErrorMessage);
                                    //Serialize as JSON for the application
                                    var jsonSerializer = new JsonSerializer();
                                    using (var ms = new MemoryStream())
                                    {
                                        var tw = new StreamWriter(ms);
                                        jsonSerializer.Serialize(tw, acceptedResult, typeof(List<ParseResultItem>));
                                        tw.Close();
                                        fields["File"].Bytes = ms.ToArray();
                                    }
                                }
                            }
                            else
                            {
                                //If it was unparsable it would be better to not submit it at all.
                                fields["File"].Bytes = new byte[0];
                                fields.Add("EXTERR_File", new MultipartFormParser.MultipartFormParser.Field
                                    {
                                        Name = "EXTERR_File",
                                        Value = "Can not read file, please make sure the file has the right format"
                                    });
                            }
                        }
                        catch (Exception)
                        {
                            //If the file contained something that caused an exception it would be better to not submit it at all.
                            fields["File"].Bytes = new byte[0];
                            fields.Add("EXTERR_File", new MultipartFormParser.MultipartFormParser.Field
                                {
                                    Name = "EXTERR_File",
                                    Value = "Can not read file, please make sure the file has the right format"
                                });
                        }
                    }
                }
                catch (Exception exception)
                {
                    if (exception is PipeException || exception is EndpointNotFoundException)
                    {
                        //If the security service is offline or broken
                        fields["File"].Bytes = new byte[0];
                        if (fields.ContainsKey("EXTERRMSG"))
                        {
                            fields["EXTERRMSG"].Value = SecurityServiceDownMsg;
                        }
                        else
                        {
                            fields.Add("EXTERRMSG", new MultipartFormParser.MultipartFormParser.Field
                                {
                                    Name = "EXTERRMSG",
                                    Value = SecurityServiceDownMsg
                                });
                        }
                    }
                    else
                    {
                        if (fields.ContainsKey("EXTERRMSG"))
                        {
                            fields["EXTERRMSG"].Value = exception.Message;
                        }
                        else
                        {
                            fields.Add("EXTERRMSG", new MultipartFormParser.MultipartFormParser.Field
                            {
                                Name = "EXTERRMSG",
                                Value = exception.Message
                            }
                                );
                        }
                    }
                }
                var post = multipart.GetMultipartFormData(fields);
                outputStream.Write(post, 0, post.Length);


                //#if DEBUG
                //                //Write output file for validation
                //                var filenameout = string.Format("post_out_{0:N}.txt", Guid.NewGuid());
                //                using (var fsout = new FileStream("c:\\" + filenameout, FileMode.CreateNew))
                //                {
                //                    fsout.Write(post, 0, post.Length);
                //                }
                //#endif

                //Relay
                //inputStream.CopyTo(outputStream);

                return true;
            });

            context.Request.Filter = new RequestFilter(context.Request.Filter, callback);
        }

        public override void PostProcessImplementation(HttpContext context)
        {
            // No post-processing
        }
    }
}
